package de.dailab.graphworld.ontology;

import java.io.Serializable;
import java.util.HashMap;

import de.dailab.jiactng.agentcore.knowledge.IFact;

/**
 * Class to save and manage the (home and gold) pheromone levels for hive intelligence agents
 * The level of decay can be set manually, per default 90% of the pheromones remain after 1000 milliseconds
 * Calculations are only made if the information is requested or updated, therefore the system time is measured and saved to get correct values
 * @author Till Herrmann
 */
public class Pheromone implements IFact{
	
	private static final long serialVersionUID = 4542208933755468315L;
	
	private class TeamPheromoneEntry implements Serializable {
		public double goldPheromone;
		public double homePheromone;
		public long goldDropTime;
		public long homeDropTime;
		
		public TeamPheromoneEntry() {
			goldPheromone = 0;
			homePheromone = 0;
			goldDropTime = 0;
			homeDropTime = 0;
		}
	}
	
	private double decay;
	private HashMap<String, TeamPheromoneEntry> pheromones;
	
	/**
	 * Default constructor, sets all pheromone levels to 0 and decay to 0,9
	 */
	public Pheromone(){
		
		this(0.90);
	}
	
	/**
	 * Specific constructor, sets all pheromone levels to 0 and decay to given value
	 * @param decay the decay rate of the pheromones
	 */
	public Pheromone(double decay){
		this.pheromones = new HashMap<String, TeamPheromoneEntry>();
		this.decay = decay;	
	}
	
	/**
	 * Method to get the current gold pheromone level depending on drop time
	 * @param teamName team name of the team who dropped the pheromone
	 * @return gold pheromone level as double
	 */
	public double getGoldPheromone(String teamName){
		TeamPheromoneEntry pheromone = pheromones.get(teamName);
		if (pheromone == null) {
			return 0;
		}
	
		double time = (System.currentTimeMillis() - pheromone.goldDropTime) / 1000;
		return( pheromone.goldPheromone * Math.pow(decay, time));
	}
	
	/**
	 * Method to get the total (from all team) gold pheromone level depending on drop time
	 * @return gold pheromone level as double
	 */
	public double getGoldPheromone() {
		double value = 0;
		for (String teamName : pheromones.keySet()) {
			value += this.getGoldPheromone(teamName);
		}
		return value;
	}
	
	/**
	 * Method to get the current home pheromone level depending on drop time
	 * @param teamName team name of the team who dropped the pheromone
	 * @return home pheromone level as double
	 */
	public double getHomePheromone(String teamName){
		TeamPheromoneEntry pheromone = pheromones.get(teamName);
		if (pheromone == null) {
			return 0;
		}
		
		double time = (System.currentTimeMillis() - pheromone.homeDropTime) / 1000;
		return( pheromone.homePheromone * Math.pow(decay, time));
	}
	
	/**
	 * Method to get the total (from all team) home pheromone level depending on drop time
	 * @return home pheromone level as double
	 */
	public double getHomePheromone() {
		double value = 0;
		for (String teamName : pheromones.keySet()) {
			value += this.getHomePheromone(teamName);
		}
		return value;
	}
	
	/**
	 * Method to update gold pheromone level and add a specific amount
	 * Existing Pheromones will be accordingly updated to match the time
	 * @param teamName team name of the team who drops the pheromone
	 * @param level the amount of pheromones to add
	 */
	public void addGoldPheromone(String teamName, double level) {
		if (!pheromones.containsKey(teamName)) {
			pheromones.put(teamName, new TeamPheromoneEntry());
		}
		TeamPheromoneEntry pheromone = pheromones.get(teamName);
		double time = (System.currentTimeMillis() - pheromone.goldDropTime) / 1000;
		double calc = pheromone.goldPheromone * Math.pow(decay, time);
		pheromone.goldDropTime = System.currentTimeMillis();
		pheromone.goldPheromone = calc + level;
	}

	
	/**
	 * Method to update home pheromone level and add a specific amount
	 * Existing Pheromones will be accordingly updated to match the time
	 * @param teamName team name of the team who drops the pheromone
	 * @param level the amount of pheromones to add
	 */
	public void addHomePheromone(String teamName, double level){
		if (!pheromones.containsKey(teamName)) {
			pheromones.put(teamName, new TeamPheromoneEntry());
		}
		TeamPheromoneEntry pheromone = pheromones.get(teamName);
		double time = (System.currentTimeMillis() - pheromone.homeDropTime) / 1000;
		double calc = pheromone.homePheromone * Math.pow(decay, time);
		pheromone.homeDropTime = System.currentTimeMillis();
		pheromone.homePheromone = calc + level;
	}

}
